// <copyright file="ESLInstruction.cs" company="Codeplex project Eco.Contrib"> 
// The MIT License (MIT) 
// </copyright> 
// <author>Peter Buchmann</author> 

namespace Eco.Contrib
{
    using System;
    using System.Collections.Generic;
    using Eco.ObjectRepresentation;
    using Eco.UmlCodeAttributes;

    /// <summary>
    /// ESLInstruction: Base class for all ESL instructions
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors", Justification = "Generated by ECO")]
    public partial class EslInstruction
    {
        /// <summary>
        /// Gets the last token.
        /// </summary>
        /// <returns>Last Token</returns>
        public EslToken LastToken
        {
            get
            {
                return this.MyTokens[this.MyTokens.Count - 1];
            }
        }

        /// <summary>
        /// Gets my tokens.
        /// </summary>
        /// <value>
        /// My tokens.
        /// </value>
        protected IList<EslToken> MyTokens { get; private set; }

        /// <summary>
        /// Asserts the instruction end.
        /// </summary>
        /// <param name="token">The token.</param>
        [UmlElement(Id = "df4eb33a-647a-4152-911a-0f9a53486a45")]
        public static void AssertInstructionEnd(EslToken token)
        {
            if (token == null)
            {
                throw new EslException("Parameter is null");
            }

            if ((token.TokenType != EslTokenType.End) &&
                 ((token.TokenType != EslTokenType.Operator) || (token.TokenValue != ";")))
            {
                throw new EslException(string.Format(System.Globalization.CultureInfo.CurrentCulture, "Unexpected {0} '{1}' at Position ({2},{3}). Expected was: {4}.", token.TokenType.ToString(), token.TokenValue, token.Line, token.Col, "Operator ; or end of script"), token);
            }
        }

        /// <summary>
        /// Executes the specified service provider.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <param name="variableList">The variable list.</param>
        /// <returns>Return value of executed function</returns>
        [UmlElement(Id = "84c4618b-490a-47e1-bc11-a1a177075f2a")]
        public IElement Execute(IEcoServiceProvider serviceProvider, IModifiableVariableList variableList)
        {
            if (serviceProvider == null)
            {
                throw new EslException("Parameter is null");
            }

            IEcoScriptLanguageService ecoScriptLanguageService = serviceProvider.GetEcoService<IEcoScriptLanguageService>();
            try
            {
                if (ecoScriptLanguageService.IsActive())
                {
                    IElement element = this.ExecuteFunction(serviceProvider, variableList);
                    if (element != null && element.Element.AsObject is ReturnElement)
                    {
                        return element;
                    }
                }
            }
            catch (Exception ex)
            {
                if (ex is EslException)
                {
                    throw;
                }
                else
                {
                    throw new EslException(string.Format(System.Globalization.CultureInfo.CurrentCulture, "Error in {0}: {1}\n at Position ({2},{3}) of Script '{4}':\n{5}", this.GetType().ToString(), ex.Message, this.ScriptPosition.Line, this.ScriptPosition.Col, this.Script.Name, this.Script.GetText(this.ScriptPosition.Pos - 1, 100)), this.ScriptPosition.Line, this.ScriptPosition.Col, this.ScriptPosition.Pos, ex);
                }
            }

            // Stop-Request;
            if (ecoScriptLanguageService.IsActive())
            {
                if (this.NextInstruction != null)
                {
                    return this.NextInstruction.Execute(serviceProvider, variableList);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Remembers the eco script.
        /// </summary>
        /// <param name="ecoScript">The eco script.</param>
        /// <param name="token">The token.</param>
        [UmlElement(Id = "6634ee0f-3add-4914-bce3-159609726995")]
        public void RememberEcoScript(EcoScript ecoScript, EslToken token)
        {
            if (token == null)
            {
                throw new EslException("Parameter is null");
            }

            this.Script = ecoScript;
            this.ScriptPosition.Line = token.Line;
            this.ScriptPosition.Col = token.Col;
            this.ScriptPosition.Pos = token.Pos;
        }

        /// <summary>
        /// Asserts the token.
        /// </summary>
        /// <param name="firstToken">The first token.</param>
        /// <param name="secondToken">The second token.</param>
        [UmlElement(Id = "b8d86454-63d2-4ee6-b56d-20d3f54a9ac1")]
        protected internal static void AssertToken(EslToken firstToken, EslToken secondToken)
        {
            if (firstToken == null || secondToken == null)
            {
                throw new EslException("Parameter is null");
            }

            if ((firstToken.TokenType != secondToken.TokenType) ||
                 ((secondToken.TokenValue != "%") && (firstToken.TokenValue != secondToken.TokenValue)))
            {
                throw new EslException(string.Format(System.Globalization.CultureInfo.CurrentCulture, "Unexpected {0} '{1}' at Position ({2},{3}). Expected was: {4}.", firstToken.TokenType.ToString(), firstToken.TokenValue, firstToken.Line, firstToken.Col, secondToken.ToString()), firstToken);
            }
        }

        /// <summary>
        /// Executes the function.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <param name="variableList">The variable list.</param>
        /// <returns>Return value of executed function</returns>
        [UmlElement(Id = "28251a57-19e9-49e1-af8c-8ac69b6c735b")]
        protected internal virtual IElement ExecuteFunction(IEcoServiceProvider serviceProvider, IModifiableVariableList variableList)
        {
            return null;
        }

        /// <summary>
        /// Inits the specified tokens.
        /// </summary>
        /// <param name="tokens">The tokens.</param>
        [UmlElement(Id = "9190bee0-3729-415c-8c74-58037866f757")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "No change of design as for now.")]
        protected internal virtual void Init(Dictionary<IList<EslToken>, Type> tokens)
        {
        }

        /// <summary>
        /// Parses the specified token list.
        /// </summary>
        /// <param name="tokenList">The token list.</param>
        /// <param name="ecoScript">The eco script.</param>
        /// <param name="fullExpression">if set to <c>true</c> [full expression].</param>
        [UmlElement(Id = "113a57d6-1e12-47a7-a277-c3424309e991")]
        protected internal virtual void Parse(IList<EslToken> tokenList, EcoScript ecoScript, bool fullExpression)
        {
            throw new EcoContribException("Class " + this.GetType().ToString() + " does not implement virtual method Parse and so is not able to be parsed.");
        }

        /// <summary>
        /// Gets the next token.
        /// </summary>
        /// <param name="tokenList">The token list.</param>
        /// <returns>Next token</returns>
        [UmlElement(Id = "4253dfdb-76d8-492c-a02d-9ab3628401c8")]
        protected EslToken GetNextToken(IList<EslToken> tokenList)
        {
            if (tokenList == null)
            {
                throw new EslException("Parameter is null");
            }

            if (tokenList.Count > 0)
            {
                EslToken token = tokenList[0];
                tokenList.RemoveAt(0);
                if (!this.MyTokens.Contains(token))
                {
                    this.MyTokens.Add(token);
                }

                return token;
            }
            else
            {
                throw new EslException("Unexpected end of program.");
            }
        }

        /// <summary>
        /// Called when objects were created.
        /// </summary>
        partial void ObjectCreated()
        {
            this.MyTokens = new List<EslToken>();
        }
    }
}